// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_DEBUG_OBJECTS_H_
#define V8_OBJECTS_DEBUG_OBJECTS_H_

#include "src/objects.h"
#include "src/objects/fixed-array.h"
#include "src/objects/struct.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

    class BreakPoint;
    class BytecodeArray;

    // The DebugInfo class holds additional information for a function being
    // debugged.
    class DebugInfo : public Struct {
    public:
        NEVER_READ_ONLY_SPACE
        enum Flag {
            kNone = 0,
            kHasBreakInfo = 1 << 0,
            kPreparedForDebugExecution = 1 << 1,
            kHasCoverageInfo = 1 << 2,
            kBreakAtEntry = 1 << 3,
            kCanBreakAtEntry = 1 << 4,
            kDebugExecutionMode = 1 << 5
        };

        using Flags = base::Flags<Flag>;

        // A bitfield that lists uses of the current instance.
        DECL_INT_ACCESSORS(flags)

        // The shared function info for the source being debugged.
        DECL_ACCESSORS(shared, SharedFunctionInfo)

        // Bit field containing various information collected for debugging.
        DECL_INT_ACCESSORS(debugger_hints)

        // Script field from shared function info.
        DECL_ACCESSORS(script, Object)

        // DebugInfo can be detached from the SharedFunctionInfo iff it is empty.
        bool IsEmpty() const;

        // --- Debug execution ---
        // -----------------------

        enum ExecutionMode { kBreakpoints = 0,
            kSideEffects = kDebugExecutionMode };

        // Returns current debug execution mode. Debug execution mode defines by
        // applied to bytecode patching. False for breakpoints, true for side effect
        // checks.
        ExecutionMode DebugExecutionMode() const;
        void SetDebugExecutionMode(ExecutionMode value);

        // Specifies whether the associated function has an instrumented bytecode
        // array. If so, OriginalBytecodeArray returns the non-instrumented bytecode,
        // and DebugBytecodeArray returns the instrumented bytecode.
        inline bool HasInstrumentedBytecodeArray();

        inline BytecodeArray OriginalBytecodeArray();
        inline BytecodeArray DebugBytecodeArray();

        // --- Break points ---
        // --------------------

        bool HasBreakInfo() const;

        // Clears all fields related to break points.
        void ClearBreakInfo(Isolate* isolate);

        // Accessors to flag whether to break before entering the function.
        // This is used to break for functions with no source, e.g. builtins.
        void SetBreakAtEntry();
        void ClearBreakAtEntry();
        bool BreakAtEntry() const;

        // The original uninstrumented bytecode array for functions with break
        // points - the instrumented bytecode is held in the shared function info.
        DECL_ACCESSORS(original_bytecode_array, Object)

        // The debug instrumented bytecode array for functions with break points
        // - also pointed to by the shared function info.
        DECL_ACCESSORS(debug_bytecode_array, Object)

        // Fixed array holding status information for each active break point.
        DECL_ACCESSORS(break_points, FixedArray)

        // Check if there is a break point at a source position.
        bool HasBreakPoint(Isolate* isolate, int source_position);
        // Attempt to clear a break point. Return true if successful.
        static bool ClearBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
            Handle<BreakPoint> break_point);
        // Set a break point.
        static void SetBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
            int source_position,
            Handle<BreakPoint> break_point);
        // Get the break point objects for a source position.
        Handle<Object> GetBreakPoints(Isolate* isolate, int source_position);
        // Find the break point info holding this break point object.
        static Handle<Object> FindBreakPointInfo(Isolate* isolate,
            Handle<DebugInfo> debug_info,
            Handle<BreakPoint> break_point);
        // Get the number of break points for this function.
        int GetBreakPointCount(Isolate* isolate);

        // Returns whether we should be able to break before entering the function.
        // This is true for functions with no source, e.g. builtins.
        bool CanBreakAtEntry() const;

        // --- Debugger hint flags ---
        // ---------------------------

        // Indicates that the function should be skipped during stepping.
        DECL_BOOLEAN_ACCESSORS(debug_is_blackboxed)

        // Indicates that |debug_is_blackboxed| has been computed and set.
        DECL_BOOLEAN_ACCESSORS(computed_debug_is_blackboxed)

        // Indicates the side effect state.
        DECL_INT_ACCESSORS(side_effect_state)

        enum SideEffectState {
            kNotComputed = 0,
            kHasSideEffects = 1,
            kRequiresRuntimeChecks = 2,
            kHasNoSideEffect = 3,
        };

        SideEffectState GetSideEffectState(Isolate* isolate);

        // Id assigned to the function for debugging.
        // This could also be implemented as a weak hash table.
        DECL_INT_ACCESSORS(debugging_id)

// Bit positions in |debugger_hints|.
#define DEBUGGER_HINTS_BIT_FIELDS(V, _)         \
    V(SideEffectStateBits, int, 2, _)           \
    V(DebugIsBlackboxedBit, bool, 1, _)         \
    V(ComputedDebugIsBlackboxedBit, bool, 1, _) \
    V(DebuggingIdBits, int, 20, _)

        DEFINE_BIT_FIELDS(DEBUGGER_HINTS_BIT_FIELDS)
#undef DEBUGGER_HINTS_BIT_FIELDS

        static const int kNoDebuggingId = 0;

        // --- Block Coverage ---
        // ----------------------

        bool HasCoverageInfo() const;

        // Clears all fields related to block coverage.
        void ClearCoverageInfo(Isolate* isolate);
        DECL_ACCESSORS(coverage_info, Object)

        DECL_CAST(DebugInfo)

        // Dispatched behavior.
        DECL_PRINTER(DebugInfo)
        DECL_VERIFIER(DebugInfo)

// Layout description.
#define DEBUG_INFO_FIELDS(V)                     \
    V(kSharedFunctionInfoOffset, kTaggedSize)    \
    V(kDebuggerHintsOffset, kTaggedSize)         \
    V(kScriptOffset, kTaggedSize)                \
    V(kOriginalBytecodeArrayOffset, kTaggedSize) \
    V(kDebugBytecodeArrayOffset, kTaggedSize)    \
    V(kBreakPointsStateOffset, kTaggedSize)      \
    V(kFlagsOffset, kTaggedSize)                 \
    V(kCoverageInfoOffset, kTaggedSize)          \
    /* Total size. */                            \
    V(kSize, 0)

        DEFINE_FIELD_OFFSET_CONSTANTS(Struct::kHeaderSize, DEBUG_INFO_FIELDS)
#undef DEBUG_INFO_FIELDS

        static const int kEstimatedNofBreakPointsInFunction = 4;

    private:
        // Get the break point info object for a source position.
        Object GetBreakPointInfo(Isolate* isolate, int source_position);

        OBJECT_CONSTRUCTORS(DebugInfo, Struct);
    };

    // The BreakPointInfo class holds information for break points set in a
    // function. The DebugInfo object holds a BreakPointInfo object for each code
    // position with one or more break points.
    class BreakPointInfo : public Tuple2 {
    public:
        // The position in the source for the break position.
        DECL_INT_ACCESSORS(source_position)
        // List of related JavaScript break points.
        DECL_ACCESSORS(break_points, Object)

        // Removes a break point.
        static void ClearBreakPoint(Isolate* isolate, Handle<BreakPointInfo> info,
            Handle<BreakPoint> break_point);
        // Set a break point.
        static void SetBreakPoint(Isolate* isolate, Handle<BreakPointInfo> info,
            Handle<BreakPoint> break_point);
        // Check if break point info has this break point.
        static bool HasBreakPoint(Isolate* isolate, Handle<BreakPointInfo> info,
            Handle<BreakPoint> break_point);
        // Get the number of break points for this code offset.
        int GetBreakPointCount(Isolate* isolate);

        int GetStatementPosition(Handle<DebugInfo> debug_info);

        DECL_CAST(BreakPointInfo)

        static const int kSourcePositionOffset = kValue1Offset;
        static const int kBreakPointsOffset = kValue2Offset;

        OBJECT_CONSTRUCTORS(BreakPointInfo, Tuple2);
    };

    // Holds information related to block code coverage.
    class CoverageInfo : public FixedArray {
    public:
        int SlotCount() const;

        int StartSourcePosition(int slot_index) const;
        int EndSourcePosition(int slot_index) const;
        int BlockCount(int slot_index) const;

        void InitializeSlot(int slot_index, int start_pos, int end_pos);
        void IncrementBlockCount(int slot_index);
        void ResetBlockCount(int slot_index);

        static int FixedArrayLengthForSlotCount(int slot_count)
        {
            return slot_count * kSlotIndexCount + kFirstSlotIndex;
        }

        DECL_CAST(CoverageInfo)

        // Print debug info.
        void Print(std::unique_ptr<char[]> function_name);

    private:
        static int FirstIndexForSlot(int slot_index)
        {
            return kFirstSlotIndex + slot_index * kSlotIndexCount;
        }

        static const int kFirstSlotIndex = 0;

        // Each slot is assigned a group of indices starting at kFirstSlotIndex.
        // Within this group, semantics are as follows:
        static const int kSlotStartSourcePositionIndex = 0;
        static const int kSlotEndSourcePositionIndex = 1;
        static const int kSlotBlockCountIndex = 2;
        static const int kSlotIndexCount = 3;

        OBJECT_CONSTRUCTORS(CoverageInfo, FixedArray);
    };

    // Holds breakpoint related information. This object is used by inspector.
    class BreakPoint : public Tuple2 {
    public:
        DECL_INT_ACCESSORS(id)
        DECL_ACCESSORS(condition, String)

        DECL_CAST(BreakPoint)

        static const int kIdOffset = kValue1Offset;
        static const int kConditionOffset = kValue2Offset;

        OBJECT_CONSTRUCTORS(BreakPoint, Tuple2);
    };

} // namespace internal
} // namespace v8

#include "src/objects/object-macros-undef.h"

#endif // V8_OBJECTS_DEBUG_OBJECTS_H_
